package cn.bonoon.kernel.security;

import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import org.batatasframework.util.RunningStatistics;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;

import cn.bonoon.entities.AccountEntity;
import cn.bonoon.kernel.FlagType;
import cn.bonoon.kernel.util.StringHelper;

/**
 * <pre>
 * 已经登录的用户信息，该信息将会存放于session里
 * </pre>
 * 
 * <pre>
 * 2013-11-28 : 新定义的登录用户信息，不再存放用户的操作权限了，当前用户操作权限的处理
 * 使用实时的读取数据库的方式
 * </pre>
 * 
 * @author jackson
 * @version 2.1 对登录用户存放的修改进行重新定义
 */
public final class StandardLogonUser extends User implements LogonUser, LogonUserRuntime {

	private static final long serialVersionUID = 5390973627983792802L;
	
	private Long id;
	private String displayName;
	private long lastUpdate;
	private FlagType flagType;
	private final Map<String, Object> attributes;
	
	/** 该账户属于一个企业,组织或其它团体之类的. */
	private final long ownerId; 
	
	private boolean needChangePwd;
	
	private String ip;
	
	private String loginAt;
	
	private RunningStatistics statistics = new RunningStatistics();
	
	private String title = "";
	
	private boolean warning;
	
//	/**
//	 * 在用户登录成功后，随机生成整数
//	 */
//	private final int seed;
	
//	private int visitCount;
//	private long timeTotal;
//	private long timeArg;
//	private long timeMax;
//	private long timeMin = Long.MAX_VALUE;

	public StandardLogonUser(AccountEntity account, Collection<? extends GrantedAuthority> authorities, boolean forcedChangePasswordEnable){
		super(account.getLoginName(), account.getLoginPwd(), account.getStatus() == AccountEntity.NORMAL, true, true, true, authorities);
		this.id = account.getId();
		
		this.ownerId = account.getOwnerId();
		this.attributes = new HashMap<String, Object>();
		this.displayName = account.getName();
		this.needChangePwd = forcedChangePasswordEnable && account.isForcedChangePwd();
		this.flagType = account.getFlag();
		this.lastUpdate = System.currentTimeMillis();
	}
	
	public void title(String title){
		if(StringHelper.isNotEmpty(title)){
			this.title = getDisplayName() + "[" + getUsername() + "] - " + title;
		}
	}

	public void warning(boolean warning){
		this.warning = warning;
	}
	
//	public StandardLogonUser(Long loginUserid, 
//			String loginUserName, 
//			String loginUserpassword, 
//			boolean enabled,
//			Collection<? extends GrantedAuthority> authorities, 
//			long ownerId, 
//			String userDisplayName,
//			boolean needChangePwd, 
//			FlagType userFlagType) {
//		
//		super(loginUserName, loginUserpassword, enabled, true, true, true, authorities);
//		
//		this.id = loginUserid;
//		this.ownerId = ownerId;
//		this.attributes = new HashMap<String, Object>();
//		this.displayName = userDisplayName;
//		this.needChangePwd = needChangePwd;
//		this.flagType = userFlagType;
//		this.lastUpdate = System.currentTimeMillis();
//	}
	
	public boolean checkTimeout(long timeout){
		return timeout + lastUpdate < System.currentTimeMillis();
	}

	public void set(String name, Object obj) {
		attributes.put(name, obj);
	}

	public Object get(String name) {
		return attributes.get(name);
	}
	
	public String getIp() {
		return ip;
	}
	
	public String getLoginAt() {
		return loginAt;
	}

	public <T> T get(String name, Class<T> cls) {
		return cls.cast(attributes.get(name));
	}
	
	@Override
	public boolean isWarning() {
		return warning;
	}
	
	public Long getLong(String name){
		Object obj = attributes.get(name);
		if(null == obj)return -1L;
		if(obj instanceof Long)return (Long)obj;
		return Long.valueOf(obj.toString());
	}
	
	public boolean contains(String name){
		return attributes.containsKey(name);
	}

	public Integer getInt(String name){
		Object obj = attributes.get(name);
		if(null == obj)return -1;
		if(obj instanceof Integer)return (Integer)obj;
		return Integer.valueOf(obj.toString());
	}
	
	public String getString(String name){
		Object obj = attributes.get(name);
		if(null == obj) return null;
		return obj.toString();
	}
	
	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public boolean allowAccess(){
		return flagType != FlagType.CUSTOM && flagType != FlagType.GUEST;
	}
	
	public String getTitle() {
		return title;
	}

	public String getDisplayName() {
		return displayName;
	}

	public Long getOwnerId() {
		return ownerId;
	}
	
	@Override
	public long toOwnerId() {
		return ownerId;
	}

	public boolean isSuper() {
		return flagType == FlagType.SUPER;
	}
	
	public boolean isAdmin(){
		return flagType == FlagType.ADMIN;
	}
	
	public boolean isUser(){
		return flagType == FlagType.USER;
	}
	
	/** 加密 */
	public final Long encryptId(Long id){
		return id;
	}
	
	public final long encryptId(long id){
		return id;
	}
	
	/** 解密 */
	public final Long decryptId(Long id){
		return id;
	}
	public final long decryptId(long id){
		return id;
	}

	public boolean needChangePwd() {
		return needChangePwd;
	}

	public void changePwd() {
		this.needChangePwd = false;
	}
	
	@Override
	public int hashCode() {
		return id.hashCode();
	}
	
	@Override
	public boolean equals(Object rhs) {
		if(rhs instanceof StandardLogonUser){
			return id.equals(((StandardLogonUser) rhs).id);
		}
		return super.equals(rhs);
	}

	@Override
	public void checkEditable() throws EditableException {
		// TODO Auto-generated method stub
		
	}
	
	private String sessionId;
	
	@Override
	public void loginSuccess(String sessionId, String ip) {
		this.ip = ip;
		this.sessionId = sessionId;
		this.loginAt = StringHelper.datetime2String(new Date());
	}
	
	public String getSessionId() {
		return sessionId;
	}
	
	@Override
	public LogonUserRuntime getRuntime() {
		return this;
	}
	
	public static void main(String[] args){
		long seed = new Random().nextLong();//0x7FFFFFFF;//System.currentTimeMillis();
		System.out.println("seed:" + seed);
		Long id = 5011l;
		System.out.println("id:" + id);
		id = seed ^ id;
		System.out.println("encrypt id:" + id);
		id = seed ^ id;
		System.out.println("decrypt id:" + id);
	}

	@Override
	public RunningStatistics getStatistics() {
		return statistics;
	}

	@Override
	public long visit(long start, long end) {
		this.lastUpdate = end;
		return statistics.statistics(start, end);
	}
}
